#region Microsoft Public License
/*
 * (c) Copyright Chenjun Wu. http://utablesdk.codeplex.com
 * 
 * This source is subject to the Microsoft Public License (Ms-PL).
 * Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
 * All other rights reserved.
 */
#endregion

﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using UTable.Input;
using UTable.Input.MultiTouch;
using UTable.Objects;
using UTable.Objects.Controls;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace UTable.Objects
{
	/// <summary>
	/// Interaction logic for UBook.xaml
	/// </summary>
	public partial class UBook : UObject
	{
        protected UBookModel Model = new UBookModel();

		public UBook()
		{
			InitializeComponent();
			this.DataContext = this;
			PagePathList = new ObservableCollection<UBookControlPage>();
			this.Loaded += new RoutedEventHandler(UBook_Loaded);
		}

        #region Private Methods

		public override object OnDataQuerying(object dataFlag)
		{
			if (dataFlag is QueryFolderPathMessage)
			{
				return this.Model.FolderPath;
			}

			return base.OnDataQuerying(dataFlag);
		}


		void UBook_Loaded(object sender, RoutedEventArgs e)
		{
            OnWorkTypeChanged();
            
            for (int i = 0; i < BookViewerControl.GetItemsCount(); i++)
            {
                UPaintCanvas canvas = GetInkCanvas(i);
                canvas.Tag = i;
                canvas.OnEventOccurred += new UPaintCanvasEventHandler(canvas_OnEventOccurred);
                canvas.DefaultDrawingAttributes.Color = PenColor;
            }
		}

        void canvas_OnEventOccurred(UPaintCanvas sender, UPaintCanvasEventArgs args)
        {
            if (args.Type == UPaintCanvasEventArgs.EventType.LineEnd)
            {
                List<Point> line = new List<Point>();
                foreach (Point p in args.Stroke.StylusPoints)
                    line.Add(p);
                NotifyModelUpdated(new UpdataBookAnnotationMessage(UpdataBookAnnotationMessage.MsgType.NewLine, (int)sender.Tag, args.LineID, line, UserId));
            }
        }

        private void OnPagesFolderPathChanged()
        {
            PagePathList.Clear();
            String[] files = Directory.GetFiles(this.Model.FolderPath);
            List<String> fileList = new List<string>(files);
            fileList.Sort();
            foreach (String file in fileList)
            {
                if (File.Exists(file))
                {
					UBookControlPage page = new UBookControlPage();
					page.PageSource = System.IO.Path.GetFullPath(file);
					PagePathList.Add(page);
                }
            }
        }

		private UPaintCanvas GetLeftInkCanvas()
		{
			int pageIndex = BookViewerControl.CurrentSheetIndex * 2 - 1;
			if (pageIndex < 0 || pageIndex >= BookViewerControl.GetItemsCount())
				return null;
			UBookControlPage page = BookViewerControl.GetPage(pageIndex) as UBookControlPage;
			return page.InkCanvas;
		}

        private UPaintCanvas GetRightInkCanvas()
		{
			int pageIndex = BookViewerControl.CurrentSheetIndex * 2;
			if (pageIndex < 0 || pageIndex >= BookViewerControl.GetItemsCount())
				return null;
			UBookControlPage page = BookViewerControl.GetPage(pageIndex) as UBookControlPage;
			return page.InkCanvas;
		}

        private static void OnWorkTypeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs pty)
		{
            (obj as UBook).OnWorkTypeChanged();
		}

        private void OnWorkTypeChanged()
		{
            switch (this.WorkType)
            {
                case UBookWorkType.Reading:
                    foreach (UBookControlPage page in this.BookViewerControl.Items)
                    {
                        page.InkCanvas.EditingMode = InkCanvasEditingMode.None;
                    }
                    break;
                case UBookWorkType.Noting:
                    foreach (UBookControlPage page in this.BookViewerControl.Items)
                    {
                        page.InkCanvas.EditingMode = InkCanvasEditingMode.Ink;
                    }
                    break;
                case UBookWorkType.Erasering:
                    foreach (UBookControlPage page in this.BookViewerControl.Items)
                    {
                        page.InkCanvas.EditingMode = InkCanvasEditingMode.EraseByStroke;
                    }
                    break;
            }
		}

        #endregion

        #region Properties

        public UBookWorkType WorkType
        {
            get { return (UBookWorkType)GetValue(WorkTypeProperty); }
            set { SetValue(WorkTypeProperty, value); }
        }

        // Using a DependencyProperty as the backing store for WorkType.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty WorkTypeProperty =
            DependencyProperty.Register("WorkType", typeof(UBookWorkType), typeof(UBook), new UIPropertyMetadata(UBookWorkType.Reading, new PropertyChangedCallback(OnWorkTypeChanged)));

        public ObservableCollection<UBookControlPage> PagePathList { get; set; }

        private Color penColor;

        public Color PenColor
        {
            get { return penColor; }
            set
            {
                penColor = value;
                for (int i = 0; i<BookViewerControl.Items.Count; i++)
                {
                    UBookControlPage page = BookViewerControl.GetPage(i) as UBookControlPage;
                    page.InkCanvas.DefaultDrawingAttributes.Color = value;
                }
            }
        }

        public int UserId { get; set; }

        #endregion

        #region Public Methods

        private UPaintCanvas GetInkCanvas(int pageIndex)
        {
            if (pageIndex < 0 || pageIndex >= BookViewerControl.GetItemsCount())
                return null;
            UBookControlPage page = BookViewerControl.GetPage(pageIndex) as UBookControlPage;
            return page.InkCanvas;
        }

        public void ClearLeftPageNote()
        {
            UPaintCanvas inkCanvas = GetLeftInkCanvas();
            if (inkCanvas == null)
                return;
            inkCanvas.Children.Clear();
            inkCanvas.Strokes.Clear();

            int pageIndex = BookViewerControl.CurrentSheetIndex * 2 - 1;
            NotifyModelUpdated(new UpdataBookAnnotationMessage(UpdataBookAnnotationMessage.MsgType.ClearPage, pageIndex, 0, null, UserId));
        }

        public void ClearRightPageNote()
        {
            UPaintCanvas inkCanvas = GetRightInkCanvas();
            if (inkCanvas == null)
                return;
            inkCanvas.Children.Clear();
            inkCanvas.Strokes.Clear();

            int pageIndex = BookViewerControl.CurrentSheetIndex * 2;
            NotifyModelUpdated(new UpdataBookAnnotationMessage(UpdataBookAnnotationMessage.MsgType.ClearPage, pageIndex, 0, null, UserId));
        }

        #endregion

		#region Share

		public override object GetModel()
        {
            return Model.Clone();
        }

        public override void UpdateModel(object updateMessage)
        {
            if (updateMessage is SetFolderPathMessage || updateMessage is OpenBookMessage)
            {
                this.Model.Clear();
                this.Model.FolderPath = (updateMessage as SetFolderPathMessage).FolderPath;
                OnPagesFolderPathChanged();
            }
            else if (updateMessage is UpdataBookAnnotationMessage)
            {
                // update the annotation
                UpdataBookAnnotationMessage msg = updateMessage as UpdataBookAnnotationMessage;
                UPaintCanvas canvas = GetInkCanvas(msg.PageIndex);
                if (canvas != null)
                {
                    if (msg.Type == UpdataBookAnnotationMessage.MsgType.NewLine)
                        canvas.AddNewStroke(msg.Line, UMeeting.GetColor(msg.UserId));
                    else
                        canvas.ClearStrokes();
                }
            }
        }

        public override void InitializeModel(object m)
        {
            UBookModel source = m as UBookModel;
            if (source != null)
            {
                this.Model = new UBookModel(source);
                OnPagesFolderPathChanged();
            }
        }

        #endregion
    }

	public class OpenBookMessage
	{
		public OpenBookMessage(String bookPath)
		{
			BookPath = bookPath;
		}

		public String BookPath { get; set; }
	}

    public class UpdataBookAnnotationMessage
    {
        public UpdataBookAnnotationMessage(MsgType type, int pageIndex, int annotationIndex, List<Point> line, int userId)
        {
            this.Type = type;
            this.PageIndex = pageIndex;
            this.AnnotationIndex = annotationIndex;
            this.Line = line;
            this.UserId = userId;
        }

        public enum MsgType
        {
            NewLine,
            ClearPage,
        }

        public MsgType Type { get; protected set; }

        public int PageIndex { get; protected set; }

        public int AnnotationIndex { get; protected set; }

        public List<Point> Line { get; protected set; }

        public int UserId { get; protected set; }
    }

    public class UBookAnnotationModel
    {
        public Dictionary<int, List<Point>> LineMap { get; set; }

        public UBookAnnotationModel()
        {
            LineMap = new Dictionary<int,List<Point>>();
        }

        public UBookAnnotationModel Clone()
        {
            UBookAnnotationModel model = new UBookAnnotationModel();
            foreach (int k in LineMap.Keys)
            {
                List<Point> t = new List<Point>();
                List<Point> s = LineMap[k];
                foreach (Point p in s)
                    t.Add(p);
                model.LineMap.Add(k, t);
            }
            return model;
        }
    }

    public class UBookModel : UObjectModel
    {
        public UBookModel()
        {
            Clear();
        }

        public UBookModel(UBookModel source)
        {
            this.FolderPath = source.FolderPath;
            this.PageIndex = source.PageIndex;
            this.Annotations = new List<UBookAnnotationModel>();
            foreach (UBookAnnotationModel a in source.Annotations)
            {
                this.Annotations.Add(a.Clone());
            }
        }

        public string FolderPath { get; set; }

        public int PageIndex { get; set; }

        public List<UBookAnnotationModel> Annotations { get; set; }

        public void Clear()
        {
            Annotations = new List<UBookAnnotationModel>();
            FolderPath = string.Empty;
            PageIndex = 0;
        }

        /// <summary>
        /// return a deep copy of the model
        /// </summary>
        public UBookModel Clone()
        {
            UBookModel model = new UBookModel(this);
            return model;
        }
    }
}
